package org.umlviewer.paint;

import org.umlviewer.view.UseCaseProperty;
import org.umlviewer.view.LifeLineProperty;
import org.umlviewer.view.ClassProperty;
import org.umlviewer.view.ActorProperty;
import org.umlviewer.view.RelationshipProperty;
import org.umlviewer.model.Constants;
import org.umlviewer.model.UmlDiagram;
import org.umlviewer.model.DrawElement;
import org.umlviewer.model.Relationship;
import org.umlviewer.model.DiagramElement;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import org.umlviewer.model.diagram_class.Association;
import org.umlviewer.model.diagram_class.Clase;
import org.umlviewer.model.sequence_diagram.LifeLine;
import org.umlviewer.model.sequence_diagram.Message;
import org.umlviewer.model.usecase_diagram.Actor;
import org.umlviewer.model.usecase_diagram.UseCase;

/**
 *
 * @author Rafa
 */
public class DiagramViewerMouseAdapter extends MouseAdapter implements ActionListener{
    
    private UmlDiagram diagram;
    private int actualAction;
    private DrawElement selectedElement;
    private Relationship rselected;
    private UMLDiagramViewer viewer;
    private boolean printingRel;
    private DiagramElement movingElement;
    private int x,y;
    private int movX,movY;
    private int pfx,pfy;
    //para el resize
    private int PROX_DIST = 3;
    private int pix,piy;
    private DiagramElement relDst;
    private JFrame parentframe;
    //el menu emergente
    private JPopupMenu jpmActions, jpmRelations;
    private JMenuItem jmiBlank,jmiProperties, jmiDelete,
            jmiBack, jmiFront, jmiCopy, jmiRelationDel, jmiRelationProps;
    //para el tipo de relacion que se agrega
    private int relType;
    private DiagramElement cloningElement;

    
    public DiagramViewerMouseAdapter(UMLDiagramViewer d) {
        this.diagram = d.getDiagram();
        this.viewer = d;
        this.parentframe = d.getParentFrame();
        //el menu emergente
        jpmActions = new JPopupMenu();
        jpmRelations = new JPopupMenu();
        jmiBlank = new JMenuItem("---");
        jmiProperties = new JMenuItem("Propiedades");
        jmiDelete = new JMenuItem("Eliminar");
        jmiBack = new JMenuItem("Enviar atras");
        jmiBack.setIcon(new ImageIcon(getClass().getResource("/org/umlviewer/view/images/ToBack.gif")));
        jmiFront = new JMenuItem("Traer al frente");
        jmiFront.setIcon(new ImageIcon(getClass().getResource("/org/umlviewer/view/images/ToFront.gif")));
        jmiCopy = new JMenuItem("Copiar");
        //jpmActions.insert(jmiBlank, 0);
        jpmActions.add(jmiProperties);        
        jpmActions.add(jmiDelete);
        jpmActions.add(jmiFront);
        jpmActions.add(jmiBack);
        jpmActions.add(jmiCopy);
        jmiProperties.addActionListener(this);  
        jmiDelete.addActionListener(this);
        jmiBack.addActionListener(this);
        jmiFront.addActionListener(this);
        jmiCopy.addActionListener(this);
        //menu de las relaciones
        jmiRelationDel = new JMenuItem("Eliminar");
        jmiRelationProps = new JMenuItem("Propiedades");
        jpmRelations.insert(jmiRelationDel, 0);
        jpmRelations.insert(jmiRelationProps, 1);
        jmiRelationDel.addActionListener(this);
        jmiRelationProps.addActionListener(this);
    }

    public int getActualAction() {
        return actualAction;
    }

    public void setActualAction(int actualAction) {
        this.actualAction = actualAction;
    }
     
    public void setPrintRel(boolean b){
        this.printingRel = b;
    }
    
    public void setDiagram(UmlDiagram dg){
        this.diagram = dg;
    }
    
    public void setRelType(int type){
        this.relType = type;
    }
    
    public DiagramElement getCloneableElement(){
        return this.cloningElement;
    }

    /***
     * Translada las coordenadas del mouse para concordar con las 
     * establecidas en la visualizcion (zoom)
     * @param evt 
     */
    private void translateMouse(MouseEvent evt){        
        double xp = evt.getX();
        double yp = evt.getY();
        double _scale = viewer.getScale();
        evt.translatePoint((int) Math.round((xp / _scale) - evt.getX()),
                (int) Math.round((yp / _scale) - evt.getY()));
    }
    
    @Override
    public void mouseClicked(MouseEvent evt) {   
         //trasladar coordenadas del mouse
        translateMouse(evt);
        
         if(evt.getClickCount()==2 && evt.getButton() == 1){
            if (actualAction == Constants.NOACTION) {
                DrawElement selde = diagram.elementAtXY(evt.getX(), evt.getY());
                if (selde == null) { //se hizo click sobre una zona en blanco
                    if (selectedElement != null) {
                        selectedElement.setSelected(false); //se deselecciona el elemento previo
                        selectedElement = null;
                    }
                } else {
                    // se cambia el anterior
                    if (selectedElement != null) {
                        selectedElement.setSelected(false);
                    }
                    selectedElement = selde;
                    selectedElement.setSelected(true);
                }
                //mostrar la inf. del elemento en la barra de estado
                printSelectd(selectedElement);
                if(selectedElement!=null)
                    showPropertiesDialog();
            }
            return;
        }        
        
        if (evt.getButton() == 1) //click izq
        {
            DiagramElement element = null;
            switch (actualAction) {
                case Constants.ADDCLASS:
                    element = new Clase("Class");
                    break;
                case Constants.ADDACTOR:
                    element = new Actor("Actor");
                    break;
                case Constants.ADDUSECASE:
                    element = new UseCase("Use Case");
                    break;
                case Constants.ADDLIFELINE:
                    element = new LifeLine("LifeLine");
                    break;
            }
            if (element != null) {
                //validar que no haya otro en esa coordenada
                element.setPosX(evt.getX());
                element.setPosY(evt.getY());
                diagram.addElement(element);
            }
            //se hace click sobre la pantalla
            if (actualAction == Constants.NOACTION) {
                DrawElement selde = diagram.elementAtXY(evt.getX(), evt.getY());
                if (selde == null) { //se hizo click sobre una zona en blanco
                    if (selectedElement != null) {
                        selectedElement.setSelected(false); //se deselecciona el elemento previo
                        selectedElement = null;
                    }
                } else {
                    // se cambia el anterior
                    if (selectedElement != null) {
                        selectedElement.setSelected(false);
                    }
                    selectedElement = selde;
                    selectedElement.setSelected(true);
                }
                //mostrar la inf. del elemento en la barra de estado
                printSelectd(selectedElement);
            }
            actualAction = Constants.NOACTION;
            viewer.repaint();
        } else if (evt.getButton() == 3) //click derecho
        {
            //movingElement = cd.elementAtXY(evt.getX(), evt.getY());
            DrawElement dw = diagram.elementAtXY(evt.getX(), evt.getY());
            if (dw != null) {
                if (dw instanceof DiagramElement) {
                    jpmActions.show(viewer, evt.getX(), evt.getY());
                } else if (dw instanceof Relationship) {
                    rselected = (Relationship) dw;
                    if (rselected != null) {
                        //JOptionPane.showMessageDialog(this, rselected.getType(), "msg", JOptionPane.INFORMATION_MESSAGE);
                        //RelationshipProperty rprop = new RelationshipProperty(null, true, rselected);
                        //rprop.setVisible(true);
                        jpmRelations.show(viewer, evt.getX(), evt.getY());
                    }
                }
            }
        }
    }

    @Override
    public void mouseDragged(MouseEvent evt) {
        translateMouse(evt);
        // si no se esta pintando una relacion, se mueve el elemento
        if (!printingRel) {

            if (movingElement != null) {
                //ajustar a la cuadricula                
                if (viewer.isAdjustGrid()) {
                    x = evt.getX();
                    y = evt.getY();
                    x = Math.round(x / viewer.getGridSize());
                    y = Math.round(y / viewer.getGridSize());
                    movX = (int) x * viewer.getGridSize();
                    movY = (int) y * viewer.getGridSize();
                } else {
                    movX = evt.getX();
                    movY = evt.getY();
                }
                movingElement.move(movX, movY);

            } else {
                //la parte del resize
                if (actualAction == Constants.NOACTION) {
                    if (selectedElement != null) {
                        Point p = evt.getPoint();
                        Rectangle r = selectedElement.getBounds();
                        int type = viewer.getCursor().getType();
                        int dx = p.x - r.x;
                        int dy = p.y - r.y;
                        switch (type) {
                            case Cursor.N_RESIZE_CURSOR:
                                int height = r.height - dy;
                                r.setRect(r.x, r.y + dy, r.width, height);
                                break;
                            case Cursor.NW_RESIZE_CURSOR:
                                int width = r.width - dx;
                                height = r.height - dy;
                                r.setRect(r.x + dx, r.y + dy, width, height);
                                break;
                            case Cursor.W_RESIZE_CURSOR:
                                width = r.width - dx;
                                r.setRect(r.x + dx, r.y, width, r.height);
                                break;
                            case Cursor.SW_RESIZE_CURSOR:
                                width = r.width - dx;
                                height = dy;
                                r.setRect(r.x + dx, r.y, width, height);
                                break;
                            case Cursor.S_RESIZE_CURSOR:
                                height = dy;
                                r.setRect(r.x, r.y, r.width, height);
                                break;
                            case Cursor.SE_RESIZE_CURSOR:
                                width = dx;
                                height = dy;
                                r.setRect(r.x, r.y, width, height);
                                break;
                            case Cursor.E_RESIZE_CURSOR:
                                width = dx;
                                r.setRect(r.x, r.y, width, r.height);
                                break;
                            case Cursor.NE_RESIZE_CURSOR:
                                width = dx;
                                height = r.height - dy;
                                r.setRect(r.x, r.y + dy, width, height);
                                break;
                            default:
                                System.out.println("unexpected type: " + type);
                        }
                        selectedElement.resize(r);
                        viewer.repaint();
                    }
                }
            }
        } else {

            if (movingElement != null) {
                pfx = evt.getX();
                pfy = evt.getY();                
                viewer.setPfx(pfx);                
                viewer.setPfy(pfy);
            }
        }
        viewer.repaint();
    }

    @Override
    public void mouseMoved(MouseEvent evt) {
        translateMouse(evt); 
        if (selectedElement != null) {
            Point p = evt.getPoint();
            if (!isOverRect(p, selectedElement.getBounds())) {
                if (viewer.getCursor() != Cursor.getDefaultCursor()) {
                    // If cursor is not over rect reset it to the default.
                    viewer.setCursor(Cursor.getDefaultCursor());
                }
                return;
            }
            // Locate cursor relative to center of rect.
            int outcode = getOutcode(p, selectedElement.getBounds());
            Rectangle r = selectedElement.getBounds();
            switch (outcode) {
                case Rectangle.OUT_TOP:
                    if (Math.abs(p.y - r.y) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.N_RESIZE_CURSOR));
                    }
                    break;
                case Rectangle.OUT_TOP + Rectangle.OUT_LEFT:
                    if (Math.abs(p.y - r.y) < PROX_DIST
                            && Math.abs(p.x - r.x) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.NW_RESIZE_CURSOR));
                    }
                    break;
                case Rectangle.OUT_LEFT:
                    if (Math.abs(p.x - r.x) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.W_RESIZE_CURSOR));
                    }
                    break;
                case Rectangle.OUT_LEFT + Rectangle.OUT_BOTTOM:
                    if (Math.abs(p.x - r.x) < PROX_DIST
                            && Math.abs(p.y - (r.y + r.height)) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.SW_RESIZE_CURSOR));
                    }
                    break;
                case Rectangle.OUT_BOTTOM:
                    if (Math.abs(p.y - (r.y + r.height)) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.S_RESIZE_CURSOR));
                    }
                    break;
                case Rectangle.OUT_BOTTOM + Rectangle.OUT_RIGHT:
                    if (Math.abs(p.x - (r.x + r.width)) < PROX_DIST
                            && Math.abs(p.y - (r.y + r.height)) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.SE_RESIZE_CURSOR));
                    }
                    break;
                case Rectangle.OUT_RIGHT:
                    if (Math.abs(p.x - (r.x + r.width)) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.E_RESIZE_CURSOR));
                    }
                    break;
                case Rectangle.OUT_RIGHT + Rectangle.OUT_TOP:
                    if (Math.abs(p.x - (r.x + r.width)) < PROX_DIST
                            && Math.abs(p.y - r.y) < PROX_DIST) {
                        viewer.setCursor(Cursor.getPredefinedCursor(
                                Cursor.NE_RESIZE_CURSOR));
                    }
                    break;
                default:    // center
                    viewer.setCursor(Cursor.getDefaultCursor());
            }
        }
    }        
    

    @Override
    public void mousePressed(MouseEvent evt) {
        translateMouse(evt);
        movingElement = diagram.getElementXY(evt.getX(), evt.getY());
        if (printingRel) {
            if (movingElement != null) {
                pix = evt.getX();
                piy = evt.getY();
                viewer.setPix(pix);
                viewer.setPiy(piy);
            }
        }
    }

    @Override
    public void mouseReleased(MouseEvent evt) {
        translateMouse(evt);
        if (evt.getButton() == 1) //click izq
        {
            if (movingElement != null) {
                relDst = diagram.getElementXY(evt.getX(), evt.getY());
                if (relDst != null) {
                    if (actualAction == Constants.ADDASSOCIATION) {
                        Association rel = new Association("", relType, movingElement, relDst);
                        diagram.addRelationship(rel);
                    } else if (actualAction == Constants.ADDMESSAGE) {
                        Message m = new Message();
                        m.setSource(movingElement);
                        m.setDestination(relDst);
                        m.setSrcPoint(new Point(pix, piy));
                        m.setDstPoint(new Point(pfx, piy));
                        diagram.addRelationship(m);
                    }
                    actualAction = Constants.NOACTION;
                    printingRel = false;
                    pix = piy = pfx = pfy = 0;
                    viewer.setPix(pix);
                    viewer.setPfx(pfx);
                    viewer.setPiy(piy);
                    viewer.setPfy(pfy);
                    viewer.repaint();
                } else //no se encontro un elemento al final
                {
                    movingElement = null;
                    actualAction = Constants.NOACTION;
                    printingRel = false;
                    pix = 0;
                    piy = 0;
                    pfx = 0;
                    pfy = 0;
                    viewer.setPix(pix);
                    viewer.setPfx(pfx);
                    viewer.setPiy(piy);
                    viewer.setPfy(pfy);
                    viewer.repaint();
                }
            } else {
                printingRel = false;
                movingElement = null;
            }
        }
    }
    
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == jmiProperties) //propiedades
        {
            showPropertiesDialog();
        }
        else if(e.getSource() == jmiDelete)
        {
            if(movingElement!=null){
                int sel = JOptionPane.showConfirmDialog(parentframe, "Desea eliminar este elemento");
                if(sel == JOptionPane.YES_OPTION){
                    diagram.removeElement(movingElement);
                    viewer.repaint();
                }
            }
        }
        else if(e.getSource() == jmiBack){
            if(movingElement!=null)
                diagram.sendToBack(movingElement);
        }
        else if(e.getSource() == jmiFront){
            if(movingElement!=null)
                diagram.bringToFront(movingElement);
        }
        else if(e.getSource()==jmiCopy){
            if(movingElement!=null){
                cloningElement = movingElement;
            }
        }
        else if(e.getSource() == jmiRelationProps){            
            RelationshipProperty rprop = new RelationshipProperty(null, true, rselected);
            rprop.setVisible(true);
        }
        else if(e.getSource() == jmiRelationDel){
            int sel = JOptionPane.showConfirmDialog(parentframe, "Desea eliminar esta relacion");
            if(sel == JOptionPane.YES_OPTION){
                diagram.removeRelationship(rselected);
                viewer.repaint();
            }
        }
    }

     /**
     * Make a smaller Rectangle and use it to locate the
     * cursor relative to the Rectangle center.
     */
    private int getOutcode(Point p, Rectangle b) {
        Rectangle r = (Rectangle)b.clone();
        r.grow(-PROX_DIST, -PROX_DIST);
        return r.outcode(p.x, p.y);       
    }
 
    /**
     * Make a larger Rectangle and check to see if the
     * cursor is over it.
     */
    private boolean isOverRect(Point p, Rectangle b) {
        Rectangle r = (Rectangle)b.clone();
        r.grow(PROX_DIST, PROX_DIST);
        return r.contains(p);
    }
    
    Clase caux;UseCase useaux; Actor actaux;LifeLine lfaux;
    private void showPropertiesDialog() {
       //aqui se puede usar una factoria para devolver un jdialog
        //dependiendo del instance of 
        if (movingElement instanceof Clase) {
            caux = (Clase) movingElement;
            ClassProperty cp = new ClassProperty(parentframe, true, caux);
            cp.setVisible(true);
        } else if (movingElement instanceof UseCase) {
            useaux = (UseCase) movingElement;
            UseCaseProperty ucp = new UseCaseProperty(parentframe, true, useaux);
            ucp.setVisible(true);
        }
        else if(movingElement instanceof Actor){
            actaux = (Actor) movingElement;
            ActorProperty acp = new ActorProperty(parentframe, true, actaux);
            acp.setVisible(true);
        }
        else if(movingElement instanceof LifeLine){
            lfaux = (LifeLine) movingElement;
            LifeLineProperty lfp = new LifeLineProperty(parentframe, true, lfaux);
            lfp.setVisible(true);
        }
    }

    private void printSelectd(DrawElement selectedElement) {
       // throw new UnsupportedOperationException("Not yet implemented");
    }
    
    

}
